﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Mvc.Ajax;
using System.Web.UI.WebControls;
using System.Web.Routing;
using CoolCode.Linq.Expressions;

namespace CoolCode.Web.Mvc
{
    /// <summary>
    /// This helper method renders a link within an HTML LI tag.
    /// A class="selected" attribute is added to the tag when
    /// the link being rendered corresponds to the current
    /// controller and action.
    /// 
    /// This helper method is used in the Site.Master View 
    /// Master Page to display the website menu.
    /// </summary>
    public static class MenuItemHelper
    {
        public static string MenuItem(this HtmlHelper helper, string linkText, string actionName, string controllerName)
        {
            return MenuItem(helper, linkText, actionName, controllerName, null, null);
        }

        public static string MenuItem(this HtmlHelper helper, string linkText, string actionName, object routeValues)
        {
            return MenuItem(helper, linkText, actionName, null, routeValues, null);
        }

        public static string MenuItem(this HtmlHelper helper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
        {
            string currentControllerName = (string)helper.ViewContext.RouteData.Values["controller"];
            string currentActionName = (string)helper.ViewContext.RouteData.Values["action"];

            var builder = new TagBuilder("li");

			if (currentControllerName.Equals(controllerName, StringComparison.CurrentCultureIgnoreCase) && currentActionName.Equals(actionName, StringComparison.CurrentCultureIgnoreCase))
			{
				// Add selected class
				builder.AddCssClass("selected");
				builder.InnerHtml = linkText;
			}
			else
			{
				// Add link
				MvcHtmlString mvcHtml = helper.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes);
				builder.InnerHtml = mvcHtml.ToHtmlString();
			}

            // Render Tag Builder
            return builder.ToString(TagRenderMode.Normal);
        }
    }

    public static class SelectedHelper
    {
        public static string Selected<T>(this HtmlHelper helper, T value1, T value2)
        {
            if (value1.Equals(value2))
                return "class=\"selected\"";
            return String.Empty;
        }
    }


    public static class ImageActionLinkHelper
    {
        public static string ImageActionLink(this AjaxHelper helper, string imageUrl, string altText, string actionName, object routeValues, AjaxOptions ajaxOptions)
        {
            var builder = new TagBuilder("img");
            builder.MergeAttribute("src", imageUrl);
            builder.MergeAttribute("alt", altText);
            var link = helper.ActionLink("[replaceme]", actionName, routeValues, ajaxOptions);
            var html = link.ToHtmlString();
            return html.Replace("[replaceme]", builder.ToString(TagRenderMode.SelfClosing));
        }
    }      

    public static class TableHelper
    {
        /// <summary>
        /// Sample：<%=Html.Table("myTable", (IList)ViewData.Model, null) %>
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="name"></param>
        /// <param name="items"></param>
        /// <param name="attributes"></param>
        /// <returns></returns>
        public static string Table(this HtmlHelper helper, string name, IList items, IDictionary<string, object> attributes)
        {
            if (items == null || items.Count == 0 || string.IsNullOrEmpty(name))
            {
                return string.Empty;
            }

            return BuildTable(name, items, attributes);
        }

        private static string BuildTable(string name, IList items, IDictionary<string, object> attributes)
        {
            StringBuilder sb = new StringBuilder();
            BuildTableHeader(sb, items[0].GetType());

            foreach (var item in items)
            {
                BuildTableRow(sb, item);
            }

            TagBuilder builder = new TagBuilder("table");
            builder.MergeAttributes(attributes);
            builder.MergeAttribute("name", name);
            builder.InnerHtml = sb.ToString();
            return builder.ToString(TagRenderMode.Normal);
        }

        private static void BuildTableRow(StringBuilder sb, object obj)
        {
            Type objType = obj.GetType();
            sb.AppendLine("\t<tr>");
            foreach (var property in objType.GetProperties())
            {
                sb.AppendFormat("\t\t<td>{0}</td>\n", property.GetValue(obj, null));
            }
            sb.AppendLine("\t</tr>");
        }

        private static void BuildTableHeader(StringBuilder sb, Type p)
        {
            sb.AppendLine("\t<tr>");
            foreach (var property in p.GetProperties())
            {
                sb.AppendFormat("\t\t<th>{0}</th>\n", property.Name);
            }
            sb.AppendLine("\t</tr>");
        }
    }


    /// <summary>
    /// Extension methods on IEnumerable.
    /// </summary>
    public static class EnumerableExtensions
    {
        static public List<ListItem> ToListItem<T>()
        {
            var li = new List<ListItem>();
            foreach (int s in Enum.GetValues(typeof(T)))
            {
                li.Add(new ListItem
                {
                    Value = s.ToString(),
                    Text = Enum.GetName(typeof(T), s)
                });
            }
            return li;
        }
        /// <summary>
        /// Converts an enumerable into a SelectList.
        /// </summary>
        /// <typeparam name="T">Type of item in the collection</typeparam>
        /// <typeparam name="TValueField">Type of the value field</typeparam>
        /// <param name="items">Items to convert into a select list</param>
        /// <param name="valueFieldSelector">Expression used to identify the data value field</param>
        /// <param name="textFieldSelector">Expression used to identify the data text field</param>
        /// <returns>A populated SelectList</returns>
        public static SelectList ToSelectList<T, TValueField>(this IEnumerable<T> items, Expression<Func<T, TValueField>> valueFieldSelector, Expression<Func<T, object>> textFieldSelector)
        {
            string textField = ExpressionToName(textFieldSelector);
            string valueField = ExpressionToName(valueFieldSelector);

            return new SelectList(items, valueField, textField);
        }

        /// <summary>
        /// Converts an enumerable into a SelectList.
        /// </summary>
        /// <typeparam name="T">Type of item in the collection</typeparam>
        /// <typeparam name="TValueField">Type of the value field</typeparam>
        /// <param name="items">Items to convert into a select list</param>
        /// <param name="valueFieldSelector">Expression used to identify the data value field</param>
        /// <param name="textFieldSelector">Expression used to identify the data text field</param>
        /// <param name="selectedValue">The selected value</param>
        /// <returns>A populated SelectList</returns>
        public static SelectList ToSelectList<T, TValueField>(this IEnumerable<T> items, Expression<Func<T, TValueField>> valueFieldSelector, Expression<Func<T, object>> textFieldSelector, TValueField selectedValue)
        {
            string textField = ExpressionToName(textFieldSelector);
            string valueField = ExpressionToName(valueFieldSelector);

            return new SelectList(items, valueField, textField, selectedValue);
        }

        /// <summary>
        /// Converts an enumerable into a SelectList.
        /// </summary>
        /// <typeparam name="T">Type of item in the collection</typeparam>
        /// <typeparam name="TValueField">Type of the value field</typeparam>
        /// <param name="items">Items to convert into a select list</param>
        /// <param name="valueFieldSelector">Expression used to identify the data value field</param>
        /// <param name="textFieldSelector">Expression used to identify the data text field</param>
        /// <param name="selectedValueSelector">A predicate that can be used to specify which values should be selected</param>
        /// <returns>A populated SelectList</returns>
        public static MultiSelectList ToSelectList<T, TValueField>(this IEnumerable<T> items, Expression<Func<T, TValueField>> valueFieldSelector, Expression<Func<T, object>> textFieldSelector, Func<T, bool> selectedValueSelector)
        {
            var selectedItems = items.Where(selectedValueSelector);
            string textField = ExpressionToName(textFieldSelector);
            string valueField = ExpressionToName(valueFieldSelector);

            return new MultiSelectList(items, valueField, textField, selectedItems);
        }

        private static string ExpressionToName<T, TValue>(Expression<Func<T, TValue>> expression)
        {
            var memberExpression = RemoveUnary(expression.Body) as MemberExpression;
            return memberExpression == null ? string.Empty : memberExpression.Member.Name;
        }

        private static Expression RemoveUnary(Expression body)
        {
            var unary = body as UnaryExpression;
            if (unary != null)
            {
                return unary.Operand;
            }
            return body;
        }
    }

    public static class DropDownListExtensions
    {
		public static MvcHtmlString DropDownListFor<T>(this HtmlHelper<T> htmlHelper, Expression<Func<T, object>> propertySpecifier, string viewDataName)
		{
			viewDataName.ThrowIfNull();
			object value = htmlHelper.ViewData[viewDataName];
			value.ThrowIfNull(viewDataName);

			if (value as IEnumerable<SelectListItem> != null)
			{
				return htmlHelper.DropDownListFor(propertySpecifier, (IEnumerable<SelectListItem>)value);
			}
			else
			{
				return htmlHelper.DropDownListFor(propertySpecifier, (IEnumerable<ValueText>)value);
			}
		}

        public static MvcHtmlString DropDownListFor<T>(this HtmlHelper<T> htmlHelper, Expression<Func<T, object>> propertySpecifier, IEnumerable<ValueText> source)
        {
            source.ThrowIfNull("source");

            string name = propertySpecifier.Body.GetMemberName();
			object value = htmlHelper.ViewData[name];

            List<SelectListItem> listInfo =  source.ToSelectList(); 

            SelectListItem selectedItem = listInfo.Where(c => c.Selected || c.Value == Convert.ToString(value) ).FirstOrDefault(); 

            MvcHtmlString html = htmlHelper.DropDownList(name, listInfo);

            if (selectedItem != null)
            {
                string replaceText = string.Format("<option value=\"{0}\">", selectedItem.Value);
                string selectedText = string.Format("<option value=\"{0}\" selected=\"selected\">", selectedItem.Value);
                string correctHtml = html.ToHtmlString().Replace(replaceText, selectedText);
                html = MvcHtmlString.Create(correctHtml);
            }

            return html;
        }
    }

    public static class CheckBoxExtensions
    {
        public static string CheckBoxListFor<T>(this HtmlHelper<T> htmlHelper, Expression<Func<T, object>> propertySpecifier)
        {
            return htmlHelper.CheckBoxListFor(propertySpecifier, 0);
        }

        public static string CheckBoxListFor<T>(this HtmlHelper<T> htmlHelper, Expression<Func<T, object>> propertySpecifier, int repeatColumns)
        {
            string propertyName = propertySpecifier.Body.GetMemberName();
            return htmlHelper.CheckBoxList(propertyName, repeatColumns);
        }

        public static string CheckBoxList(this HtmlHelper htmlHelper, string name)
        {
            return htmlHelper.CheckBoxList(name, 0);
        }

        public static string CheckBoxList(this HtmlHelper htmlHelper, string name, int repeatColumns)
        {
            object source = htmlHelper.ViewData[name];
            source.ThrowIfNull(name);

            List<SelectListItem> listInfo = new List<SelectListItem>();

            if (source.GetType() == typeof(List<SelectListItem>))
            {
                listInfo = (List<SelectListItem>)source;
            }
            else if (source.GetType() == typeof(List<ValueText>))
            {
                listInfo = ((List<ValueText>)source).ToSelectList();
            }
            return htmlHelper.CheckBoxList(name, listInfo, repeatColumns);
        }

        public static string CheckBoxList(this HtmlHelper htmlHelper, string name, List<SelectListItem> listInfo, int repeatColumns)
        {
            listInfo.ThrowIfNull();
            return htmlHelper.CheckBoxList(name, listInfo, ((IDictionary<string, object>)null), repeatColumns);
        }

        public static string CheckBoxList(this HtmlHelper htmlHelper, string name, List<SelectListItem> listInfo, object htmlAttributes)
        {
            return htmlHelper.CheckBoxList(name, listInfo, ((IDictionary<string, object>)new RouteValueDictionary(htmlAttributes)), 0);
        }

        public static string CheckBoxList(this HtmlHelper htmlHelper, string name, List<SelectListItem> listInfo, IDictionary<string, object> htmlAttributes, int repeatColumns)
        {
            if (String.IsNullOrEmpty(name))
                throw new ArgumentException("The argument must have a value", "name");
            if (listInfo == null)
                throw new ArgumentNullException("listInfo");
            //if (listInfo.Count ()< 1)
            //    throw new ArgumentException("The list must contain at least one value", "listInfo");

            Func<int, SelectListItem, string> buildCheckBox = (index, info) =>
            {
                TagBuilder checkboxBuilder = new TagBuilder("input");
                if (info.Selected)
                {
                    checkboxBuilder.MergeAttribute("checked", "checked");
                }
                checkboxBuilder.MergeAttributes<string, object>(htmlAttributes);
                checkboxBuilder.MergeAttribute("type", "checkbox");
                checkboxBuilder.MergeAttribute("value", info.Value);
                checkboxBuilder.MergeAttribute("name", name);
                checkboxBuilder.GenerateId(name + "_" + index);

                TagBuilder labelBuilder = new TagBuilder("label");
                labelBuilder.MergeAttribute("for", checkboxBuilder.Attributes["id"]);
                labelBuilder.InnerHtml = info.Text;

                string result = checkboxBuilder.ToString(TagRenderMode.SelfClosing) + labelBuilder.ToString(TagRenderMode.Normal);
                return result;
            };

            int i = 0;
            StringBuilder sb = new StringBuilder();

            if (repeatColumns <= 0)
            {
                foreach (SelectListItem info in listInfo)
                {
                    sb.Append(buildCheckBox(i++, info));
                }
            }
            else
            {
                int columnIndex = 0;
                sb.Append("<table border=\"0\">");
                foreach (SelectListItem info in listInfo)
                {
                    i++;
                    columnIndex++;
                    bool isBeginColumn = columnIndex == 1;
                    bool isEndColumn = columnIndex == repeatColumns;

                    if (isBeginColumn)
                    {
                        sb.Append("<tr>");
                    }

                    sb.Append("<td>");
                    sb.Append(buildCheckBox(i++, info));
                    sb.Append("</td>");

                    if (isEndColumn)
                    {
                        sb.Append("</tr>");
                        columnIndex = 0;
                    }
                }
                sb.Append("</table>");
            }

            return sb.ToString();
        }

        public static List<SelectListItem> ToSelectList(this IEnumerable<ValueText> source)
        {
            List<SelectListItem> listInfo = source
                .Select(c => new SelectListItem
                {
                    Value = c.Value,
                    Text = c.Text,
                    Selected = c.Selected
                })
                .ToList();
            return listInfo;
        }
    }

	public static class OtherExtensions
	{
		public static MvcHtmlString Copyright(this HtmlHelper htmlHelper, int beginYear)
        { 
			int currentYear = DateTime.Now.Year ;
			if (currentYear > beginYear)
			{
				return MvcHtmlString.Create(string.Format("{0}-{1}", beginYear, currentYear));
			}
			return MvcHtmlString.Create( beginYear.ToString());
        }
	}

    //// This the information that is needed by each checkbox in the
    //// CheckBoxList helper.
    //public class CheckBoxListInfo
    //{
    //    public CheckBoxListInfo(string value, string displayText, bool isChecked)
    //    {
    //        this.Value = value; 
    //        this.DisplayText = displayText;
    //        this.IsChecked = isChecked;
    //    }

    //    public string Value { get; private set; }
    //    public string DisplayText { get; private set; }
    //    public bool IsChecked { get; private set; }
    //}

    //public class CheckBoxList : MvcHtmlString
    //{
    //    HtmlHelper _helper;
    //    string[] _items;
    //    public CheckBoxList(HtmlHelper helper, params string[] items)
    //    {
    //        _helper = helper;
    //        _items = items;
    //    }

    //    public override string ToString()
    //    {
    //         return base.ToString();
    //    }
    //}

}
